#ifndef PHYSICS_OBJECT_H
#define PHYSICS_OBJECT_H

#include <OgreVector3.h>


namespace pong {
	class CollisionInfo;

	/**Physics Objects have 3D position, velocity, and acceleration properties, all of which have accessors and modifiers,
	   as well as a mass value.
	**/
	class PhysicsObject {
	protected:
		Ogre::Vector3 positionVec, velocityVec, accelerationVec; //Vectors required for motion
		
		Ogre::AxisAlignedBox hitBox; //Collision box

		float mass; //Floating-point mass value (point-mass)

	public:
		///Constructor given initial motion, mass, and bounds
		PhysicsObject(float _mass = 0.0F, const Ogre::AxisAlignedBox& _hitBounds = Ogre::AxisAlignedBox::BOX_NULL, 
					  const Ogre::Vector3& p0 = Ogre::Vector3::ZERO, 
					  const Ogre::Vector3& v0 = Ogre::Vector3::ZERO, 
					  const Ogre::Vector3& a0 = Ogre::Vector3::ZERO
					 )
			: mass(_mass), 
			  positionVec(p0), velocityVec(v0), accelerationVec(a0),
			  hitBox(_hitBounds)
		{}
		///Copy constructor
		PhysicsObject(const PhysicsObject& other) 
			: mass(other.mass), 
			  positionVec(other.positionVec), velocityVec(other.velocityVec), accelerationVec(other.accelerationVec)
		{}

		//Accessors for the position, velocity, and acceleration vectors
		inline const Ogre::Vector3& position(void) const {return positionVec;}
		inline const Ogre::Vector3& velocity(void) const {return velocityVec;}
		inline const Ogre::Vector3& acceleration(void) const {return accelerationVec;}
		//Setters for the motion vectors
		virtual void setPosition(const Ogre::Vector3& newPos) {positionVec = newPos;}
		virtual void setVelocity(const Ogre::Vector3& newVel) {velocityVec = newVel;}
		virtual void setAcceleration(const Ogre::Vector3& newAccel) {accelerationVec = newAccel;}

		//Accessor for the AxisAlignedBox representing a basic collision model
		inline const Ogre::AxisAlignedBox& hitVolume(void) const {return hitBox;}

		//Checks for collision between this object and another based on their AABB's, by default.
		virtual bool checkCollision(const PhysicsObject& otherObj) const {
			return hitBox.intersects(otherObj.hitBox);
		}
		//Reacts to given collision information. By default, no action is taken.
		virtual void collisionResponse(CollisionInfo& colInfo) {
		}
	};

	/** StaticObjects are "supermassive" immobile entities. They cannot be moved, but can
		be collided against and have their positions set.
	**/
	class StaticObject : public PhysicsObject {
	public:
		StaticObject(const Ogre::Vector3& p0 = Ogre::Vector3::ZERO, const Ogre::AxisAlignedBox& _hitBox = Ogre::AxisAlignedBox::BOX_NULL)
			: PhysicsObject(Ogre::Math::POS_INFINITY, _hitBox, p0) 
		{}

		//Override setters to do nothing
		void setVelocity(const Ogre::Vector3& newVel) {}
		void setAcceleration(const Ogre::Vector3& newAccel) {}
	};

	/**CollisionInformation contains all the important data from a collision, including
	   the normal and the velocity of the object concerned. The object is copied over, and so
	   does not affect the other object in any way. Consequently, this ensures that both objects 
	   can realisitically react to colliding against each other, as they are not dependent on each
	   other's current status.
	**/
	class CollisionInfo {
	protected:
		//The normal of intersection and the velocity of the incoming object as 3D vectors
		Ogre::Vector3 normalVec;
		PhysicsObject collidingObject;

	public:
		//CollisionInfo must be supplied with the normal and velocity of 
		CollisionInfo(const Ogre::Vector3& collisionNormal, PhysicsObject& _object) 
			: normalVec(collisionNormal), collidingObject(_object)
		{}

		//Accessor for the normal of intersection
		const Ogre::Vector3& normal(void) {return normalVec;}
		//Accessor for the object concerned
		PhysicsObject& object(void) {return collidingObject;}
	};

}

#endif